package com.hualan.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*;

public class DruidUtil {

    private static DataSource dataSource;

    static{
        try {
            //DruidUtil.class.getClassLoader() : 获得类加载器
            //.getResourceAsStream("jdbc.properties") : 到类路径下读取jdbc.properties文件
            InputStream inputStream = DruidUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties properties = new Properties();//用来存在properties配置文件内容的对象
            properties.load(inputStream);//参数：输入流  使用输入流读取properties文件的内容，存储到Properties对象中、
            dataSource = DruidDataSourceFactory.createDataSource(properties);//参数1：properties对象
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 通过数据连接池-----获取连接对象--不过是事务版
    public static Connection getConn(){
        try {
            Connection connection = dataSource.getConnection();
            connection.setAutoCommit(true);//设置事务是否自动提交  true:自动提交，默认值  false:手动提交
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 释放全部资源----需要传三个参数
    public static void release(Connection connection, Statement statement, ResultSet resultSet){
        closeRS(resultSet);
        closeST(statement);
        closeConn(connection);
    }

    // 释放查询结果资源
    public static void closeRS(ResultSet resultSet){
        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                resultSet = null;
            }
        }
    }

    // 释放statement资源
    public static void closeST(Statement statement){
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                statement=null;
            }
        }
    }

    // 释放连接对象
    public static void closeConn(Connection connection){
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                connection=null;
            }
        }
    }

    // druid里没有QueryRunner工具包，所以需要自己封装
    // 增删改方法----->通用
    public static int update(String sql, Object... parameters){
        int row = 0;
        Connection con = null;
        try {
            con = dataSource.getConnection();
            PreparedStatement statement = con.prepareStatement(sql);
            // 给sql语句中的 ? 赋值
            for (int i = 0; i < parameters.length; i++) {
                statement.setObject(i + 1, parameters[i]);
            }
            row = statement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return row;
    }

    /*
     * 事务版--通用的增, 删, 改方法
     * @param sql  增删改sql语句
     * @param con  连接对象
     * @param parameters sql语句中?的值
     * @return 返回sql语句执行之后,影响的行数
     */
    // 需要传con对象
    public static int update(String sql,Connection con, Object... parameters) {
        int row = 0;
        try(PreparedStatement stmt = con.prepareStatement(sql)){
            //给sql语句中的?赋值
            for(int i = 0; i < parameters.length; i++) {
                stmt.setObject(i + 1, parameters[i]);
            }
            row = stmt.executeUpdate();
        }catch(Exception e) {
            e.printStackTrace();
        }
        return row;
    }

    // 查询函数---->通用
    public static <T> List<T> query(String sql, Class<T> clazz, boolean underlineToCamel, Object... parameters){
        List<T> list = new ArrayList<>();
        Connection con = null;
        try {
            con = dataSource.getConnection();
            PreparedStatement statement = con.prepareStatement(sql);
            // 给sql语句中的 ? 赋值
            for (int i = 0; i < parameters.length; i++) {
                statement.setObject(i + 1, parameters[i]);
            }
            // 获取结果集
            ResultSet resultSet = statement.executeQuery();
            // 结果集的元数据
            ResultSetMetaData rsmd = resultSet.getMetaData();
            // 获取查询的列数
            int columnCount = rsmd.getColumnCount();
            // 创建一个string数组, 存放查询的所有列名称
            String[] columnLabelArr = new String[columnCount];
            for (int i = 1; i <= columnCount; i++) {
                // 获取所有的标签名(列名)
                String columnLabel = rsmd.getColumnLabel(i);
                columnLabelArr[i -1] = columnLabel;   // 数组下标从0开始
            }
            // 遍历查询结果
            while (resultSet.next()){
                // 通过反射创建对象
                Constructor<T> constructor = clazz.getDeclaredConstructor();
                constructor.setAccessible(true);  // 强制访问
                T obj = constructor.newInstance();
                for (int i = 0; i < columnLabelArr.length; i++) {
                    // 通过下标获取列名
                    String columnLabel = columnLabelArr[i];
                    // 列对应的值
                    Object value = resultSet.getObject(columnLabel);
                    // 通过反射获取类中的field
                    if (underlineToCamel){
                        // 下划线转成小驼峰命名
                        columnLabel = underlineToCamel(columnLabel);
                    }
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    // 通过反射给属性赋值
                    if (value != null){
                        field.set(obj, value);
                    }
                }
                list.add(obj);
            }
            resultSet.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    /*
     * 事务版--通用查询方法
     * @param sql 查询的sql语句
     * @param con 连接对象
     * @param clazz 希望封装成什么类型 例如希望封装成 Student类型,参数写成 Student.class
     * @param underlineToCamel 是否需要把表中字段带_的转换成小驼峰名称 true表示转, false表示不转
     * @param parameters  sql语句中?的值
     * @return 返回一个List,list是clazz对应类型的对象
     */
    public static <T> List<T> query(String sql,Connection con, Class<T> clazz , boolean underlineToCamel,Object...parameters){
        List<T> list = new ArrayList<>();
        try(PreparedStatement stmt = con.prepareStatement(sql);){
            //给sql语句中的?赋值
            for(int i = 0; i < parameters.length; i++) {
                stmt.setObject(i + 1, parameters[i]);
                System.out.println("事务版德鲁伊查询：" + parameters[i]);
            }
            System.out.println("传过来的sql语句：" + sql);
            //获取结果集
            ResultSet resultSet = stmt.executeQuery();

            //结果集的元数据
            ResultSetMetaData rsmd = resultSet.getMetaData();
            //获取查询的列数
            int columnCount = rsmd.getColumnCount();
            //创建一个String数组,存放查询的所有列的列名
            String[] columnLabelArr = new String[columnCount];
            for(int i = 1; i <= columnCount; i++) {
                //获取列的标签名(列名)
                String columnLabel = rsmd.getColumnLabel(i);
                columnLabelArr[i-1] = columnLabel;
                System.out.println("列名：" + columnLabel);
            }
            System.out.println("result值：" + resultSet);
            //遍历查询结果
            while(resultSet.next()) {
                //通过反射创建对象!
                Constructor<T> constructor = clazz.getDeclaredConstructor();
                constructor.setAccessible(true); //跳过访问权限检查
                T obj = constructor.newInstance();

                for(int i = 0; i < columnLabelArr.length; i++) {
                    //通过下标获取列名
                    String columnLabel = columnLabelArr[i];
                    //列对应的值
                    Object value = resultSet.getObject(columnLabel);
//                    System.out.println("德鲁伊中列对应的值：" + value);

                    //通过反射获取类中的Field
                    if(underlineToCamel) {
                        //下划线转小驼峰
                        columnLabel = underlineToCamel(columnLabel);
                    }
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);//跳过访问权限的检查
                    //通过反射给属性赋值
                    if(value != null) {
                        field.set(obj, value);
                    }
                }
                list.add(obj);
            }
            resultSet.close();

        }catch(Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    // 查询方法----->通用
    public static <T> List<T> query(String sql, Class<T> clazz, Object... parameters){
        return query(sql, clazz, true, parameters);
    }

    /*
     * 事务版---通用查询方法
     * @param sql 查询的sql语句
     * @param con 连接对象
     * @param clazz 希望封装成什么类型 例如希望封装成 Student类型,参数写成 Student.class
     * @param parameters  sql语句中?的值
     * @return 返回一个List,list是clazz对应类型的对象
     */
    public static <T> List<T> query(String sql,Connection con ,Class<T> clazz ,Object...parameters){
        return query(sql,con ,clazz, true, parameters);
    }

    // 查询单个值-------->通用查询
    public static <T> T queryOne(String sql, Class<T> clazz, boolean underlineToCamel, Object... parameters){
        List<T> list = query(sql, clazz, underlineToCamel, parameters);
        return list.size() == 0 ? null : list.get(0);
    }

    /*
     * 事务版---查询单个值的通用查询
     * @param sql  查询的sql语句
     * @param con 连接对象
     * @param clazz  希望封装成的类型
     * @param underlineToCamel 是否需要把表中字段带_的转换成小驼峰名称 true表示转, false表示不转
     * @param parameters sql语句中?的值
     * @return  如果没有查到返回null,如果查到了返回第一个结果
     */
    public static <T>  T queryOne(String sql,Connection con, Class<T> clazz, boolean underlineToCamel, Object... parameters) {
        List<T> list = query(sql,con, clazz,underlineToCamel, parameters);
        return list.size() == 0 ? null : list.get(0);
    }

    // 查询单个值------->通用查询
    public static <T> T queryOne(String sql, Class<T> clazz, Object... parameters){
        List<T> list = query(sql, clazz, true, parameters);
        return list.size() == 0 ? null : list.get(0);
    }

    /*
     * 事务版---查询单个值的通用查询
     * @param sql  查询的sql语句
     * @param con 连接对象
     * @param clazz  希望封装成的类型
     * @param parameters sql语句中?的值
     * @return  如果没有查到返回null,如果查到了返回第一个结果
     */
    public static <T>  T queryOne(String sql,Connection con ,Class<T> clazz, Object... parameters) {
        List<T> list = query(sql,con, clazz, true, parameters);
        return list.size() == 0 ? null : list.get(0);
    }

    // 下划线转换成小驼峰
    private static String underlineToCamel(String underline) {
        //创建一个可变字符串
        StringBuilder sb = new StringBuilder(underline);
        //遍历字符串中的每个字符
        for(int i = 0; i < sb.length(); i++) {
            char c = sb.charAt(i);
            if(c == '_' && i != sb.length() - 1) {
                //如果是下划线,找到它下个字符
                char nextChar = underline.charAt(i+1);
                //把这个字符变成大写
                nextChar = Character.toUpperCase(nextChar);
                //替换字符
                sb.setCharAt(i+1, nextChar);
            }
        }
        String str = sb.toString();
        return str.replace("_","");
    }

    // 查询方法------>通用
    public static List<Map<String, Object>> query(String sql, Object... parameters){
        List<Map<String, Object>> list = new ArrayList<>();
        try {
            Connection con = dataSource.getConnection();
            PreparedStatement statement = con.prepareStatement(sql);
            // 给sql中?赋值
            for (int i = 0; i < parameters.length; i++) {
                statement.setObject(i + 1, parameters[i]);
            }
            // 获取结果集
            ResultSet resultSet = statement.executeQuery();
            // 获取结果集元数据
            ResultSetMetaData rsmd = resultSet.getMetaData();
            // 获取总列数
            int columnCount = rsmd.getColumnCount();
            // 创建一个string数组, 存放全部的列名
            String[] columnNames = new String[columnCount];
            for (int i = 1; i <= columnCount; i++) {
                // 通过列号获取列名
                String columnLabel = rsmd.getColumnLabel(i);  // 获取列名
                columnNames[i - 1] = columnLabel;   // 将列名存进数组
            }
            // 遍历结果集
            while (resultSet.next()){
                Map<String, Object> map = new HashMap<String, Object>();
                // 遍历每一列
                for (int i = 0; i < columnNames.length; i++) {
                    String columnName = columnNames[i];  // 获取列名
                    Object value = resultSet.getObject(columnName);  // 获取这一列的值
                    // 把列名和值存入map
                    map.put(columnName, value);
                }
                list.add(map);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    /*
     * 事务版---通用查询方法
     * @param sql 查询的sql语句
     * @param con 连接对象
     * @param parameters  sql语句中?的值
     * @return 返回一个List,list的元素是Map类型
     */
    public static List<Map<String, Object>> query(String sql,Connection con ,Object...parameters){
        List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
        try(PreparedStatement stmt = con.prepareStatement(sql);){
            //给sql语句中的?赋值
            for(int i = 0; i < parameters.length; i++) {
                stmt.setObject(i + 1, parameters[i]);
            }
            //执行查询,获取结果集
            ResultSet resultSet = stmt.executeQuery();
            //获取结果集元数据
            ResultSetMetaData rsmd = resultSet.getMetaData();
            //获取查询的列数
            int columnCount = rsmd.getColumnCount();
            //创建一个String数组,存放列名
            String[] columnNames = new String[columnCount];
            for(int i = 1; i <= columnCount; i++) {
                //通过列号 获取列名
                String columnLabel = rsmd.getColumnLabel(i);
                columnNames[i-1] = columnLabel;
            }
            //遍历结果集
            while(resultSet.next()) {
                Map<String, Object> map = new HashMap<String, Object>();
                //遍历每一列
                for(int i = 0; i < columnNames.length; i++) {
                    //获取列名
                    String columnName = columnNames[i];
                    //通过列名获取值
                    Object value = resultSet.getObject(columnName);
                    //把列名和值存入map中
                    map.put(columnName, value);
                }
                list.add(map);
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    // 查询方法------>通用
    public static Map<String, Object> queryOne(String sql, Object... parameters){
        List<Map<String, Object>> list = query(sql, parameters);
        return list.size() == 0 ? null : list.get(0);
    }

    /*
     * 事务版---通用查询方法
     * @param sql 查询的sql语句
     * @param con 连接对象
     * @param parameters  sql语句中?的值
     * @return 返回一个Map类型
     */
    public static Map<String, Object> queryOne(String sql, Connection con,Object...parameters){
        List<Map<String, Object>> list = query(sql, con, parameters);
        return list.size() == 0 ? null : list.get(0);
    }
}
